package mainSocket;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.Socket;
import java.net.SocketException;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import parser.DeleteParser;
import parser.InsertParser;
import parser.SelectParser;
import parser.UpdateParser;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import storage.DataAccessManager;
import storage.UserAccessManager;
import entity.User;

public class Listener implements Runnable 
{
	public Socket clientSocket;
	private String id = null;
	private String pw = null;
	private UserAccessManager userData;
	private DataAccessManager databaseData;
	private boolean isValidated;
	public Listener(Socket ss) 
	{
		clientSocket=ss;
		userData=new UserAccessManager();
		databaseData = new DataAccessManager();
		isValidated = false;
	}
	
	public String validateQuery(String query)
	{
		return "true";
	}

	public JTable processQuery(String query)
	{
		
		return null;
	}
	
	public DefaultComboBoxModel getDatabaseModel()
	{
		DefaultComboBoxModel model = new DefaultComboBoxModel();
		databaseData.refreshList();
		for (int i=0;i<databaseData.getDatabaseListSize();i++) model.addElement(databaseData.getDatabase(i).getName());
		return model;
	}
	@Override
	public void run() 
	{
		// TODO Auto-generated method stub
        try 
        {
			System.out.println("Talking to client");
			System.out.println(clientSocket.getLocalAddress().toString());
			OutputStream os=clientSocket.getOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(os);
			InputStream is=clientSocket.getInputStream();
			ObjectInputStream ois = new ObjectInputStream(is);
			
			
			String receivedMessage;
			
			while (!isValidated)
			{
				receivedMessage=(String) ois.readObject();
				System.out.println("Received : " + receivedMessage);
				id = "";
				pw = "";
				int index=0;
				for(int i=0;i<receivedMessage.length();i++)
				{
					if(receivedMessage.charAt(i)!=' ')
					{
						id+=receivedMessage.charAt(i);
					}
					else
					{
						index = i;
						break;
					}			
				}
				for(int i=index+1;i<receivedMessage.length();i++) pw += receivedMessage.charAt(i);			
				User a=new User(id, pw);
				int check=userData.isUserExists(id);
				if (check==-1)
				{
					oos.writeObject(new String("user"));
					
				}
				boolean chek2=userData.isPasswordMatching(id, pw);
				if(chek2==false)
				{
					oos.writeObject(new String("password"));
				} 
				else 
				{
					System.out.println("Client has joined !");
					isValidated = true;
					oos.writeObject(new String("done"));
					break;
				}
			}
			oos.writeObject(getDatabaseModel());
			do
			{
				receivedMessage=(String) ois.readObject();
				databaseData.refreshList();
				if (receivedMessage!=null)
				{
					System.out.println("Received : " + receivedMessage);
					if (receivedMessage.equalsIgnoreCase("INSERT"))
					{
						processInsert(ois,oos);
					}
					else if (receivedMessage.equalsIgnoreCase("DELETE"))
					{
						processDelete(ois, oos);
					}
					else if (receivedMessage.equalsIgnoreCase("UPDATE"))
					{
						processUpdate(ois,oos);
					}
					else if (receivedMessage.equalsIgnoreCase("SELECT"))
					{
						processSelect(ois,oos);
					}
					
					if (receivedMessage.equalsIgnoreCase("quit")) break;
				}
			}
			while (true);
			ois.close();
			oos.close();
        }
        catch (SocketException SE)
        {
        	System.out.println("Client lost");
        }
        catch (Exception e) 
        {
            e.printStackTrace();
        }
        System.out.println("Finished on " + Thread.currentThread());
	}
	

	private boolean compare(String value,String compareValue,String operator)
	{
		if (compareValue.equals("") && value==null) return true;
		else if (value==null) return false;
		if (operator.equals("="))
		{
			return value.equalsIgnoreCase(compareValue);
		}
		else if (operator.equals("<>"))
		{
			return !value.equalsIgnoreCase(compareValue);
		}
		else if (operator.equals(">="))
		{
			return (value.compareToIgnoreCase(compareValue)>=0);
		}
		else if (operator.equals("<="))
		{
			return (value.compareToIgnoreCase(compareValue)<=0);
		}
		else if (operator.equals(">"))
		{
			return (value.compareToIgnoreCase(compareValue)>0);
		}
		else if (operator.equals("<"))
		{
			return (value.compareToIgnoreCase(compareValue)<0);
		}
		return false;
	}
	private void processInsert(ObjectInputStream ois,ObjectOutputStream oos)
	{
		try 
		{
			String databaseName = (String)ois.readObject();
			String query = (String)ois.readObject();
			CCJSqlParserManager pm = new CCJSqlParserManager();
			Insert insertStatement = (Insert) pm.parse(new StringReader(query));
			InsertParser insertParser = new InsertParser(databaseName);
			oos.writeObject(insertParser.insert(insertStatement));
		}
		catch (JSQLParserException jpe)
		{
			try 
			{
				oos.writeObject("Syntax error. There are mistakes in the query.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (ClassNotFoundException e) 
		{
		} 
		catch (IOException e) 
		{
			System.out.println("Client lost");
		}
	}

	private void processDelete(ObjectInputStream ois, ObjectOutputStream oos) 
	{
		try 
		{
			String databaseName = (String)ois.readObject();
			String query = (String)ois.readObject();
			CCJSqlParserManager pm = new CCJSqlParserManager();
			Delete delStatement = (Delete) pm.parse(new StringReader(query));
			DeleteParser delParser = new DeleteParser(databaseName);
			String result = delParser.getModel(delStatement);
			oos.writeObject(result);
			
		}
		catch (JSQLParserException jpe)
		{
			try 
			{
				oos.writeObject("Syntax error. There are mistakes in the query.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (ClassNotFoundException e) 
		{
		} 
		catch (IOException e) 
		{
			System.out.println("Client lost");
		}
	}
	
	private void processUpdate(ObjectInputStream ois,ObjectOutputStream oos)
	{
		try 
		{
			String databaseName = (String)ois.readObject();
			String query = (String)ois.readObject();
			CCJSqlParserManager pm = new CCJSqlParserManager();
			Update updateStatement = (Update) pm.parse(new StringReader(query));
			UpdateParser updateParser = new UpdateParser(databaseName);
			String result = updateParser.update(updateStatement);
			oos.writeObject(result);
		}
		catch (JSQLParserException jpe)
		{
			try 
			{
				oos.writeObject("Syntax error. There are mistakes in the query.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (ClassNotFoundException e) 
		{
		} 
		catch (IOException e) 
		{
			System.out.println("Client lost");
		}
	}
	
	private void processSelect( ObjectInputStream ois,ObjectOutputStream oos) 
	{
		try 
		{
			String databaseName = (String)ois.readObject();
			String query = (String)ois.readObject();
			CCJSqlParserManager pm = new CCJSqlParserManager();
			Select selectStatement = (Select) pm.parse(new StringReader(query));
			SelectParser selectParser = new SelectParser(databaseName);
			DefaultTableModel model = selectParser.getModel(selectStatement);
			oos.writeObject(new String("success"));
			oos.writeObject(model);
			
		}
		catch (JSQLParserException jpe)
		{
			try 
			{
				oos.writeObject(new String("fail"));
				oos.writeObject("Syntax error. There are mistakes in the query.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (ArrayIndexOutOfBoundsException aioobe)
		{
			try 
			{
				oos.writeObject(new String("fail"));
				oos.writeObject("Select Error. Can not find table or column name.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (NullPointerException npe)
		{
			try 
			{
				oos.writeObject(new String("fail"));
				oos.writeObject("Select Error. Can not find table or column name.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (NumberFormatException nfe)
		{
			try 
			{
				oos.writeObject(new String("fail"));
				oos.writeObject("Select Error. Invalid function on an non-numeric column.");
				return;
			} 
			catch (IOException e) 
			{
				System.out.println("Client lost");
			}
		}
		catch (ClassNotFoundException e) 
		{
		} 
		catch (IOException e) 
		{
			System.out.println("Client lost");
		}
		
		
	}

}
